/*
 * Part of knowledge engineering (ke) course work, 2010/11
 */
package se.bth.ke.firstfriend.web;

import de.nixis.commons.web.base.Controller;
import java.util.ArrayList;
import se.bth.ke.firstfriend.dto.ArticleInfo;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response.Status;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.en.EnglishAnalyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.util.Version;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.Search;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import se.bth.ke.firstfriend.dto.SearchResult;
import se.bth.ke.firstfriend.model.Article;
import static se.bth.ke.firstfriend.util.Util.PRODUCES_HTML;

/**
 *
 * @author nico
 */
@Component
@Path("/search")
public class SearchController extends Controller {

    private static final Version LUCENE_VERSION = Version.LUCENE_31;
    
    @PersistenceContext
    private EntityManager em;
    
    private QueryParser parser;

    @POST
    @Transactional
    @Produces(PRODUCES_HTML)
    public Object searchViaPost(@FormParam("term") String query) {
        return render("/search/search", 
            new SearchResult(query, getArticleInfosByQueryString(query))
        );
    }

    @GET
    @Transactional
    @Produces("application/json")
    public List<ArticleInfo> searchViaGet(@QueryParam("term") String query) {
        return getArticleInfosByQueryString(query);
    }
    
    /**
     * Finds articles matching a given query string
     * @param queryString
     * @return
     */
    private List<ArticleInfo> getArticleInfosByQueryString(String queryString) {
        // create native Lucene query
        org.apache.lucene.search.Query query;

        try {
             query = getQueryParser().parse(queryString);
        } catch (ParseException e) {
            throw new WebApplicationException(e, Status.BAD_REQUEST);
        }

        // wrap Lucene query in a javax.persistence.Query and execute
        // search
        List<Article> articles = getFullTextEntityManager()
                                    .createFullTextQuery(query, Article.class)
                                    .getResultList();


        // Wrap article in article info (which is ready to be sent over the wire
        // as json or xml
        List<ArticleInfo> results = new ArrayList();
        for (Article a: articles) {
            results.add(new ArticleInfo(a));
        }

        return results;
    }

    private FullTextEntityManager getFullTextEntityManager() {
        return Search.getFullTextEntityManager(em);
    }

    private QueryParser getQueryParser() {
        if (parser == null) {
            String[] fields = new String[] { "title", "content", "comments.content", "topics.name" };
            Analyzer analyzer = new EnglishAnalyzer(LUCENE_VERSION, StandardAnalyzer.STOP_WORDS_SET);

            parser = new MultiFieldQueryParser(LUCENE_VERSION, fields, analyzer);
        }

        return parser;
    }
}
